package middlewares

import (
	"errors"
	"net"
	"net/http/httputil"
	"os"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"

	"github.com/runningwater/gohub/pkg/logger"
	"github.com/runningwater/gohub/pkg/response"
)

// Recovery 使用 zap.Error() 来记录 panic 和 call stack
func Recovery() gin.HandlerFunc {

	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {

				// 获取用户的请求信息
				request, _ := httputil.DumpRequest(c.Request, true)

				// 链接中断，客户端中断连接为正常行为，不需要记录堆栈信息
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					var se *os.SyscallError
					if errors.As(ne.Err, &se) {
						errStr := strings.ToLower(se.Error())
						if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") {
							brokenPipe = true
						}
					}
				}
				// 链接中断的情况
				if brokenPipe {
					logger.Error(c.Request.URL.Path,
						zap.Time("time", time.Now()),
						zap.Any("error", err),
						zap.String("request", string(request)),
					)
					_ = c.Error(err.(error)) //nolint:errcheck
					c.Abort()
					// 链接已断开，无法写状态
					return
				}
				// 如果不是链接中断的情况，记录堆栈信息
				logger.Error("[Recovery from panic]",
					zap.Time("time", time.Now()),           // 记录当前时间
					zap.Any("error", err),                  // 记录错误信息
					zap.String("request", string(request)), // 记录请求信息
					zap.Stack("stack"),                     // 记录堆栈信息
				)
				// c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
				// 	"message": "服务器内部错误，请稍后再试",
				// })
				response.Abort500(c)
			}
		}()

		// 继续处理请求
		c.Next()
	}
}
